package org.real.guetshop.utils;

import lombok.Data;
import lombok.SneakyThrows;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

public class SHAUtils {

    private static final int SALT_LENGTH = 16;      // 定义盐值的长度（以字节为单位）

    private static final String CHARSET = "UTF-8";  // 指定字符集，用于将字符串转换为字节数组

    private static final String HASH_ALGORITHM = "SHA-256";      // 指定哈希算法为SHA-256

    /**
     * 使用随机盐值对密码进行哈希编码。
     * @param password 明文密码
     * @return PasswordHash 对象，包含哈希后的密码和盐值
     */
    @SneakyThrows
    public static PasswordHash encodePasswordWithSalt(String password) {
        // 创建一个安全的随机数生成器以生成盐值
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[SALT_LENGTH];
        random.nextBytes(salt); // 生成随机盐值

        try {
            MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);

            // 将明文密码转换为字节数组
            byte[] passwordBytes = password.getBytes(CHARSET);

            //存储盐和密码的组合
            byte[] saltedPassword = new byte[passwordBytes.length + salt.length];
            System.arraycopy(passwordBytes, 0, saltedPassword, 0, passwordBytes.length);
            // 复制盐值字节到新数组的相应位置
            System.arraycopy(salt, 0, saltedPassword, passwordBytes.length, salt.length);

            // 计算组合后的密码和盐值的哈希
            byte[] hashedBytes = digest.digest(saltedPassword);
            // 对得到的哈希值进行Base64编码
            String encodedHash = Base64.getEncoder().encodeToString(hashedBytes);
            // 对盐值也进行Base64编码
            String encodedSalt = Base64.getEncoder().encodeToString(salt);

            return new PasswordHash(encodedHash, encodedSalt);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Error encoding password with SHA-256", e);
        }
    }

    /**
     * 验证提供的明文密码是否与存储的加盐哈希密码匹配。
     * @param plainPassword 用户输入的明文密码
     * @param storedHash 存储的密码哈希对象，包含之前计算的哈希和盐值
     * @return 密码是否匹配
     */
    @SneakyThrows
    public static boolean verifyPassword(String plainPassword, PasswordHash storedHash) {
        try {
            // 获取SHA-256消息摘要实例
            MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
            // 解码存储的盐值
            byte[] decodedSalt = Base64.getDecoder().decode(storedHash.getSalt());

            // 创建一个新数组用于合并明文密码字节和盐值字节
            byte[] combined = new byte[plainPassword.getBytes(CHARSET).length + decodedSalt.length];
            // 复制明文密码字节和盐值字节到新数组
            System.arraycopy(plainPassword.getBytes(CHARSET), 0, combined, 0, plainPassword.getBytes(CHARSET).length);
            System.arraycopy(decodedSalt, 0, combined, plainPassword.getBytes(CHARSET).length, decodedSalt.length);

            // 计算合并后的数组的哈希值
            byte[] computedHash = digest.digest(combined);
            // 对计算出的哈希值进行Base64编码
            String encodedComputedHash = Base64.getEncoder().encodeToString(computedHash);

            // 比较计算出的哈希值与存储的哈希值是否一致
            return encodedComputedHash.equals(storedHash.getHash());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Error verifying password with SHA-256", e);
        }
    }

    @Data
    // 内部类，用于封装哈希后的密码和盐值
    public static class PasswordHash {
        private final String hash; // 哈希后的密码
        private final String salt; // 盐值
    }
}